<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
<html>
  <head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=yes">
  
  
  <title>  React实现异步加载数据 |   XHX 博客 </title>

 
  
    <link rel="icon" href="/images/favicon.ico">
  


  <link rel="stylesheet" href="/nayo.min.css"> 
</head>  
  <body>   
    
      <header class="header">
	
  <nav class="header-nav">        

    <span class="iconfont icon-menu mobile-toggle"></span>   	

    <div class="header-logo">
      <a href="/">
        <img class="header-logo-img" src="/images/head.JPG">
      </a>
    </div>

    <div class="header-menu">          
              
          
            <a class="header-menu-link" id="header-menu-home" href="/">
              <i class="iconfont icon-home">  
            </i></a>     
          
              
          
            <a class="header-menu-link" id="header-menu-archives" href="/archives">
              <i class="iconfont icon-archives">  
            </i></a>     
          
              
          
            <a class="header-menu-link" id="header-menu-tags" href="/tags">
              <i class="iconfont icon-tags">  
            </i></a>     
          
              
          
            <a class="header-menu-link" id="header-menu-about" href="/about">
              <i class="iconfont icon-about">  
            </i></a>     
          
              
          
              <a class="header-menu-link" id="header-menu-search">
                <i class="iconfont icon-search">  
              </i></a>
          
                  
    </div>  
    
  </nav>
</header>

   

      <div class="container">       
          
          
            <section class="main">  
          

          <article class="post">
  
	<div class="post-header">

	<p class="post-title">	
		React实现异步加载数据
	</p>
			

	<div class="meta-info">	
	<span>
		Jun 13, 2018
	</span>

	
	
		<i class="iconfont icon-words"></i>
		<span>
			2437
		</span>
	
</div>

</div> 
	 

	  <div class="typo post-content slideDownMin">

		

			
					<h1 id="零、引言"><a href="#零、引言" class="headerlink" title="零、引言"></a>零、引言</h1><p>​    熟悉了 React 编写组件的方式，我们自然就想到了一个实际工程中非常常见的一个需求：网络条件差、而请求数据量大的时候，需要的是页面先显示出来，然后异步请求数据填充进去。这样用户体验会更好。<br><a id="more"></a></p>
<h1 id="一、后台程序-API-实现"><a href="#一、后台程序-API-实现" class="headerlink" title="一、后台程序 API 实现"></a>一、后台程序 API 实现</h1><p>​    用 java 写一个简单的 WebAPI 返回数据，这里我们以昨天的步骤条为例，返回一个步骤条的三个步骤的内容数据。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@WebServlet</span>(urlPatterns = <span class="string">"/steps"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">StepApi</span> <span class="keyword">extends</span> <span class="title">HttpServlet</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">service</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span></span>&#123;</span><br><span class="line">        response.setCharacterEncoding(<span class="string">"utf-8"</span>);</span><br><span class="line">        response.setHeader(<span class="string">"Access-Control-Allow-Origin"</span>, <span class="string">"*"</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//get the data;</span></span><br><span class="line">        List&lt;StepModel&gt; steps = getStepData();</span><br><span class="line">        String json = JSONObject.toJSONString(steps);</span><br><span class="line">        <span class="comment">//+延时</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">        &#125;<span class="keyword">catch</span> (Exception e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            PrintWriter out = response.getWriter();</span><br><span class="line">            out.append(json);</span><br><span class="line">            out.flush();</span><br><span class="line">            out.close();</span><br><span class="line">        &#125;<span class="keyword">catch</span> (Exception e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>​    为了更好的观察效果，我们对这个接口加上 1S 的延迟。</p>
<p>​    结果如下（假设我们做的是支付宝提现的步骤）：</p>
<p><img src="https://xiaohaoxing-1257815318.cos.ap-chengdu.myqcloud.com/blog/steps-api-response.png" alt="请求结果"></p>
<h1 id="二、React-组件的更新"><a href="#二、React-组件的更新" class="headerlink" title="二、React 组件的更新"></a>二、React 组件的更新</h1><p>​    这里我们主要用到的一个方法是：componentDidMount()。顾名思义，就是组件加载完毕之后执行的方法。我们这里顺便用上了 ES6 的Promise 语法（实际作用有待验证！）</p>
<p>​    首先写一个通用的获取数据方法readData：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> readData = <span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">getSteps</span>(<span class="params">url</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">let</span> response = <span class="keyword">await</span> fetch(url);</span><br><span class="line">    <span class="keyword">let</span> json = <span class="keyword">await</span> response.text();</span><br><span class="line">    <span class="keyword">return</span> json;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>​    这个函数是 async 的，因此返回的是一个 Promise 对象，需要 readData.then()对数据继续操作。</p>
<p>​    然后我们需要在组件内部（就是 Progress 这个 class 里面）实现 componentDidMount 方法：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">componentDidMount()&#123;</span><br><span class="line">    readData(<span class="string">"http://localhost:8080/steps"</span>).then(<span class="function"><span class="params">data</span>=&gt;</span>&#123;</span><br><span class="line">        data = <span class="built_in">JSON</span>.parse(data);</span><br><span class="line">        <span class="keyword">this</span>.setState(&#123;<span class="attr">steps</span>: data&#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>​    接着，我们要把 render 方法里写 steps 的地方修改一点点，之前用的是全局变量 steps，我们这里设置 steps 为 this.state.steps 就 OK 了。（const {current} = this.state 也要改为const current = this.state.current 哦，因为 state 成员不止一个了嘛。）</p>
<p>​    最后，我们需要给这个组件设定一个数据的 placeholder。在组件的constructor 函数（构造函数）里，state 增加一个 steps 字段，值设置为数据没加载出来时期望的内容（比如：Step1、Step2这样的）。</p>
<p>​    总结起来一共4点：</p>
<ol>
<li><p>编写 async 的获取数据的函数。</p>
</li>
<li><p>组件实现 componentDidMount 方法。</p>
</li>
<li><p>render 里的数据获取方式从固定值变成从 state 里面提取。</p>
</li>
<li><p>设置 state 里数据的初始值。</p>
<p>记得看看有没有遗漏！</p>
</li>
</ol>
<h1 id="三、运行情况和调试"><a href="#三、运行情况和调试" class="headerlink" title="三、运行情况和调试"></a>三、运行情况和调试</h1><p>​    然后我们就可以用 <code>npm start</code>  命令启动项目啦。😄看看是否首先看到组件的内容为 placeholder ，过了1s 才显示出我们需要的数据呢？</p>
<blockquote>
<p>​    (我的运行结果)</p>
</blockquote>
<p><img src="https://xiaohaoxing-1257815318.cos.ap-chengdu.myqcloud.com/blog/react-async-request.gif" alt=""></p>
<p>​    如果没有按照我们期望的显示怎么办？☹️我们就有必要对前端进行调试了。调试的方法也很简单，我们监控 state 的变化情况，就掌控了运行有没有按照我们的计划运行。我们在 render 的 return 之前增加一行语句：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">debugger</span>;</span><br></pre></td></tr></table></figure>
<p>​    打开浏览器的开发者工具，刷新页面。</p>
<p>​    这样，浏览器在运行到这里的时候就会自动停止下来，进入断点。这个时候再在浏览器的调试器界面监视 <code>this.state</code> 就可以看到数据是否被正确取出。</p>
<p><img src="https://xiaohaoxing-1257815318.cos.ap-chengdu.myqcloud.com/blog/safari-debugger.png" alt=""></p>
<p>​    如上图，我们就可以看到，第一次进入 debugger 的时候数据是 placeholder 里的，第二次的时候就是来自于我们异步请求的数据了。</p>
<h1 id="四、总结"><a href="#四、总结" class="headerlink" title="四、总结"></a>四、总结</h1><p>​    数据是组件的灵魂。我们采用异步的方式去加载组件数据，在网络加载缓慢的情况下仍然可以预览到提示信息，效果等同于 img 标签的 alt。也比较符合现在异步加载的趋势。</p>
<h1 id="五、后续"><a href="#五、后续" class="headerlink" title="五、后续"></a>五、后续</h1><p>​    计划将这个项目部署到 github，并且在项目基础上进一步完善。能做出一个完整的项目。</p>
<h1 id="六、更新"><a href="#六、更新" class="headerlink" title="六、更新"></a>六、更新</h1><p>​    项目让<a href="http://ygy.online" target="_blank" rel="noopener">@光耀</a>大佬过目以后，他指出项目中异步获取的方式有问题。具体就是：async 和 await 配合使用可以解决 Promise 写法外部读取不到返回的数据的问题，这里继续采用 then 去处理数据就失去了写 async 的意义了。因此修改了这部分的代码。具体变动见<a href="https://github.com/xiaohaoxing/react-app/commit/6247658eabffebdf188b84ebb1201d9b9d700978" target="_blank" rel="noopener">github</a>。变化不多但是涉及的原理挺深入的。还需要继续学习呀！</p>
  	
					
	  </div>     
	  

	<div class="post-footer">

  <div class="post-footer-other">
      <span class="post-footer-item">
        




<span class="donate-btn">
	<span class="iconfont icon-donate"></span>
</span>


<div id="donate-box" class="sildeUpMin">

	<span class="donate-cancel iconfont icon-cancel"></span>

	<div class="donate-img-box">
		<img id="donate-qr-wechat" class="noLazyLoad donate-img" src="/images/donate1.png" alt="No Donate Image!">	
		<img id="donate-qr-alipay" class="noLazyLoad donate-img lazyload" src="/images/placeholder.png" alt="No Donate Image!" data-src="/images/donate2.png">	
	</div>

	<span class="donate-word">希望本文对你有帮助~</span>

	<div class="donate-list">
		<span class="iconfont icon-donate-wechat"></span>
		<span class="iconfont icon-donate-alipay"></span>
	</div>

</div>

 
      </span>           
      <span class="post-footer-item">
        
	
<script id="-mob-share" src="http://f1.webshare.mob.com/code/mob-share.js?appkey=29a4323345125"></script>
	
	<span class="share-btn">
	<span class="iconfont icon-share"></span>
	</span>


	<div class="-mob-share sildeUpMin">
		<div class="-mob-inner">
		   			             
            <a class="iconfont icon-share-qq -mob-share-link"></a>		
     	   			             
            <a class="iconfont icon-share-weixin -mob-share-link"></a>		
     	   			             
            <a class="iconfont icon-share-weibo -mob-share-link"></a>		
     	   
		</div>
	</div>	

      </span>           
  </div>  

  <div class="post-footer-meta">
        	

        
          <i class="iconfont icon-tag"></i>     
            <a class="tag-link" href="/tags/React/">React</a> <a class="tag-link" href="/tags/前端/">前端</a>    
        	
  </div>

</div>


<nav class="post-footer-nav">
  
    <div class="post-footer-link">
      <a href="/2018/07/02/Mac-OS上搭建虚拟机集群/" id="post-nav-newer" class="post-nav-link-wrap">
        <strong class="post-nav-caption">newer</strong>
           
          <a class="post-nav-title" href="/2018/07/02/Mac-OS上搭建虚拟机集群/">
          Mac OS上搭建网络连通的虚拟机集群😊</a>
      </a>
    </div>
  
  
    <div class="post-footer-link">
      <a href="/2018/06/11/First-React-Project/" id="post-nav-older" class="post-nav-link-wrap">
        <strong class="post-nav-caption">older</strong>
        
          <a class="post-nav-title" href="/2018/06/11/First-React-Project/">
          First React Project</a>
      </a>
    </div>
  
</nav>
 
	


    
    <div id="disqus_thread"></div>

    <script>
    (function() { 
    var d = document, s = d.createElement('script');
    s.src = 'https://'+'xiaohaoxing-github-io'+'.disqus.com/embed.js';
    s.setAttribute('data-timestamp', +new Date());    
    (d.head || d.body).appendChild(s);
    })();
    </script>

    <noscript>Please enable JavaScript to view the  <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a>
    </noscript>


	
</article>

          </section> 
      </div>            
    
    <a id="backTop">
      <span>
        <i class="iconfont icon-backtotop"></i>
      </span>
    </a> 

  
    
    <div class="search-container sildeUpMin">
        <div class="search-header">
            <input type="text" placeholder="输入你想搜索的" id="search-input" class="search-input">
            <span class="search-cancel">
                <i class="iconfont icon-cancel">
            </i></span>
        </div>
        <div id="search-result" class="search-result"></div>
    </div>
 
     <div class="mobile-menu">      

      
      <img class="mobile-menu-icon lazyload" src="/images/placeholder.png" data-src="/images/favicon.ico">   
      

         
            

            <a class="mobile-menu-link" href="/">首頁
            </a>
            
         
            

            <a class="mobile-menu-link" href="/archives">歸檔
            </a>
            
         
            

            <a class="mobile-menu-link" href="/tags">標籤
            </a>
            
         
            

            <a class="mobile-menu-link" href="/about">關於
            </a>
            
         
                          

            <a class="mobile-menu-link mobile-menu-search" href="#">搜索 </a>                 
            
         
      
</div>        
    



     
    




<footer id="footer">	    

		
		<div class="footer-copyright">
		&copy;
				
		2018-
		
		2019		
	
		XiaoHaoxing
		<br>

		Theme By
		<a href="https://github.com/Lemonreds/hexo-theme-Nayo" target="_blank">Nayo</a>	
		</div>			
		<script src="https://s19.cnzz.com/z_stat.php?id=1274862886&web_id=1274862886" language="JavaScript"></script>
</footer>   

  

    <script src="/nayo.bundle.js"></script>           
  </body>        
</html>